package com.zui.manager;


import ij.ImagePlus;
import ij.process.ImageProcessor;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.zui.entity.product.Image;
import com.zui.exception.ApplicationException;
import com.zui.util.ZuiConstants;
import com.zui.webapp.action.TradeAction;

/**
 * 
 * @author Sam
 */
@Transactional
public class ImageManagerImpl extends EntityManagerImpl<Image, String> implements
		ImageManager {
	
	private static final Log log = LogFactory.getLog(TradeAction.class);
	
    public static final int IMGAGE_MEDIUM_SIZE = 268;
    public static final int IMGAGE_SMALL_SIZE = 45;
    public static final int IMGAGE_LIST_VIEW_WIDTH = 100;
    public static final int IMGAGE_LIST_VIEW_HEIGHT = 75;
    public static final int IMGAGE_THUMB_VIEW_SIZE = 210;
    public static final int IMGAGE_MAXIMUM_WIDTH = 1440;
    public static final int IMGAGE_MAXIMUM_HEIGHT = 1200;
    
	/**
	 * Generate resied image based on original image, write to file system and save info into database.
	 * @param image
	 */
	public void addImage(Image image) {
		
		//Get image id as image name
		this.saveOrUpdate(image);
		
		File directory = new File(image.getDirectory());
		if (!directory.exists())
			directory.mkdirs();	
		
		try {
			File originalImageFile = new File(image.getDirectory(),image.getOrginalImageFileName());
			FileUtils.copyFile(image.getUploadedFile(), originalImageFile);
			
			BufferedImage originalImage, mediumImage, smallImage, listViewImage, thumbViewImage;
			
			//Generate resied image based on original image.
			originalImage = ImageIO.read(originalImageFile);
			ImagePlus imagePlus = new ImagePlus();
			imagePlus.setImage(originalImage);
			
			//write the resized original image to file system.
			ImageProcessor originalImageProcessor = resizeToMaxAllowedSize(imagePlus);
			if (originalImageProcessor!=null)
				ImageIO.write(originalImageProcessor.getBufferedImage(), image.getImageFormat(), originalImageFile);
			
			//resize and crop image to square.
			mediumImage = resizeAndCropToSquare(imagePlus, IMGAGE_MEDIUM_SIZE).getBufferedImage();
			smallImage = resizeAndCropToSquare(imagePlus, IMGAGE_SMALL_SIZE).getBufferedImage();
			listViewImage = resizeAndCropToRectangle(imagePlus, IMGAGE_LIST_VIEW_WIDTH, IMGAGE_LIST_VIEW_HEIGHT).getBufferedImage();
			thumbViewImage = resizeAndCropToSquare(imagePlus, IMGAGE_THUMB_VIEW_SIZE).getBufferedImage();
			
			ImageIO.write(smallImage, image.getImageFormat(), new File(image.getDirectory(),image.getSmallImageFileName()));
			ImageIO.write(mediumImage, image.getImageFormat(), new File(image.getDirectory(),image.getMediumImageFileName()));
			ImageIO.write(listViewImage, image.getImageFormat(), new File(image.getDirectory(),image.getListViewImageFileName()));
			ImageIO.write(thumbViewImage, image.getImageFormat(), new File(image.getDirectory(),image.getThumbViewImageFileName()));
			
			//set the orginal image file size
			image.setOriginalImageFileSize(new Long(originalImageFile.length()).intValue());
		} catch (IOException e) {
			log.error("processProductImage() error!", e.getCause());
			throw new ApplicationException("Procee product image exception!");
		}
		this.saveOrUpdate(image);
	}
	/**
	 * Creates a new ImageProcessor containing a scaled copy 
	 *	of this image or ROI with the aspect ratio maintained 
	 *	and the averaging occurs to avoid aliasing artifacts;
	 *  if width is larger than height. keep height as the dstSize and crop the width.
	 * @param imagePlus
	 * @param dstWidth
	 * @return null if no resize or resized imageProcessor.
	 */
	public ImageProcessor resizeToMaxAllowedSize(ImagePlus imagePlus) {
		ImageProcessor ip = null;
		// if width is larger than height. keep height as the dstSize
		if (imagePlus.getWidth()>imagePlus.getHeight() && imagePlus.getWidth()>IMGAGE_MAXIMUM_WIDTH) {
			ip = imagePlus.getProcessor().resize(
					IMGAGE_MAXIMUM_WIDTH,
					(int)(IMGAGE_MAXIMUM_WIDTH*((double)imagePlus.getHeight()/imagePlus.getWidth())),
					true);
		} else if (imagePlus.getWidth()<imagePlus.getHeight() 
				&& imagePlus.getHeight()>IMGAGE_MAXIMUM_HEIGHT )
			ip = imagePlus.getProcessor().resize(
					(int)((IMGAGE_MAXIMUM_HEIGHT*(double)imagePlus.getWidth()/imagePlus.getHeight())),
					IMGAGE_MAXIMUM_HEIGHT,
					true);
		
		return ip;
	}	
	/**
	 * Creates a new ImageProcessor containing a scaled copy 
	 *	of this image or ROI with the aspect ratio maintained 
	 *	and the averaging occurs to avoid aliasing artifacts;
	 *  if width is larger than height. keep height as the dstSize and crop the width.
	 * @param imagePlus
	 * @param dstWidth
	 * @return
	 */
	public ImageProcessor resizeToMinSize(ImagePlus imagePlus, int dstSize) {
		ImageProcessor ip = null;
		// if width is larger than height. keep height as the dstSize
		if (imagePlus.getWidth()>imagePlus.getHeight())
			ip = imagePlus.getProcessor().resize(
					(int)((double)dstSize*imagePlus.getWidth()/imagePlus.getHeight()),dstSize, true);
		// if height is larger than width. keep width as the dstSize
		else if (imagePlus.getWidth()<imagePlus.getHeight())
			ip = imagePlus.getProcessor().resize(
					dstSize,
					(int)((double)dstSize*imagePlus.getHeight()/imagePlus.getWidth()),
					true);
		else
			ip = imagePlus.getProcessor().resize(dstSize,dstSize,true);
		
		return ip;
	}
	/**
	 * Creates a new ImageProcessor containing a scaled copy 
	 *	of this image or ROI with the aspect ratio maintained 
	 *	and the averaging occurs to avoid aliasing artifacts;
	 *  if width is larger than height. keep height as the dstSize and crop the width.
	 * @param imagePlus
	 * @param dstWidth
	 * @param dstHeight dstWidth > dstHeight
	 * @return
	 */
	public ImageProcessor resizeToRectangleSize(ImagePlus imagePlus, int dstWidth, int dstHeight) {
		ImageProcessor ip = null;
		// if width is larger than height. keep height as the dstHeight
		if (imagePlus.getWidth()>imagePlus.getHeight())
			ip = imagePlus.getProcessor().resize(
					(int)((double)dstHeight*imagePlus.getWidth()/imagePlus.getHeight()),dstHeight, true);
		// if height is larger than or equals width. keep width as the dstWidth
		else if (imagePlus.getWidth()<=imagePlus.getHeight())
			ip = imagePlus.getProcessor().resize(
					dstWidth,
					(int)((double)dstWidth*imagePlus.getHeight()/imagePlus.getWidth()),
					true);	

		return ip;
	}
	/**
	 * Creates a new ImageProcessor containing a scaled and cropped copy 
	 *	of this image or ROI with the dstSize width and dstSize height, 
	 *  with the aspect ratio maintained and the averaging occurs to avoid aliasing artifacts;
	 *  if width is larger than height. keep height as the dstSize and crop the width.
	 * @param imagePlus
	 * @param dstWidth
	 * @return
	 */
	public ImageProcessor resizeAndCropToSquare(ImagePlus imagePlus, int dstSize) {
		ImageProcessor ip = this.resizeToMinSize(imagePlus, dstSize);
		if (ip.getWidth()>ip.getHeight())
			ip.setRoi((ip.getWidth() - dstSize)/2, 0, dstSize, dstSize);
		else if (ip.getWidth()<ip.getHeight())
			ip.setRoi(0, (ip.getHeight() - dstSize)/2, dstSize, dstSize);
		else
			return ip;
		
		return ip.crop();
	}
	/**
	 * Creates a new ImageProcessor containing a scaled and cropped copy 
	 *	of this image or ROI with the dstSize width and dstSize height, 
	 *  with the aspect ratio maintained and the averaging occurs to avoid aliasing artifacts;
	 *  if width is larger than height. keep height as the dstSize and crop the width.
	 * @param imagePlus
	 * @param dstWidth
	 * @return
	 */
	public ImageProcessor resizeAndCropToRectangle(ImagePlus imagePlus, int dstWidth, int dstHeight) {
		ImageProcessor ip = this.resizeToRectangleSize(imagePlus, dstWidth, dstHeight);
		if (ip.getWidth()>dstWidth)
			ip.setRoi((ip.getWidth() - dstWidth)/2, 0, dstWidth, dstHeight);
		else if (ip.getHeight()>dstHeight)
			ip.setRoi(0, (ip.getHeight() - dstHeight)/2, dstWidth, dstHeight);
		else
			return ip;
		
		return ip.crop();
	}	
	public void deleteImage(Image image) {

		this.deleteEntity(deleteImageFromFileSystem(image));
	}
	
	public Image deleteImageFromFileSystem(Image image) {
		image = this.loadById(image.getId());
		
		File originalImageFile = new File(image.getDirectory(),image.getOrginalImageFileName());
		if (originalImageFile.exists())	originalImageFile.delete();
		
		File smallImageFile = new File(image.getDirectory(),image.getSmallImageFileName());
		if (smallImageFile.exists()) smallImageFile.delete();
		
		File mediumImageFile = new File(image.getDirectory(),image.getMediumImageFileName());
		if (mediumImageFile.exists()) mediumImageFile.delete();
		
		File listViewImageFile = new File(image.getDirectory(),image.getListViewImageFileName());
		if (listViewImageFile.exists()) listViewImageFile.delete();
		
		File thumbViewImageFile = new File(image.getDirectory(),image.getThumbViewImageFileName());
		if (thumbViewImageFile.exists()) thumbViewImageFile.delete();
		
		return image;
	}

}
